CPU 使用率高就一定有效率吗?
背景
最近碰到一个客户业务跑在8C ECS 上,随着业务压力增加 CPU使用率也即将跑满,于是考虑将 8C 升级到16C,事实是升级后业务 RT 反而略有增加,这个事情也超出了所有程序员们的预料,所以我们接下来分析下这个场景
分析
通过采集升配前后、以前和正常时段的火焰图对比发现CPU 增加主要是消耗在 自旋锁上了:
用一个案例来解释下自旋锁和锁,如果我们要用多线程对一个整数进行计数,要保证线程安全的话,可以加锁(synchronized), 这个加锁操作也有人叫悲观锁,抢不到锁就让出这个线程的CPU 调度(代价上下文切换一次,几千个时钟周期)
另外一种是用自旋锁(CAS、spin_lock) 来实现,抢不到锁就耍赖占住CPU 死磕不停滴抢(CPU 使用率一直100%),自旋锁的设计主要是针对抢锁概率小、并发低的场景。这两种方案针对场景不一样各有优缺点
假如你的机器是8C,你有100个线程来对这个整数进行计数的话,你用synchronized 方式来实现会发现CPU 使用率永远达不到50%
1 | #taskset -a -c 56-63 java LockAccumulator 100 1000000000 |
如果我们改成自旋锁版本的实现,8个核CPU 都是100%
以下代码累加次数只有加锁版本的10%,时间还长了很多,也就是效率产出实在是低
1 | #taskset -a -c 56-63 java -Djava.library.path=. SpinLockAccumulator 100 100000000 |
代码
我放在了github 上,有个带调X86 平台 pause 指令的汇编,Java 中要用JNI 来调用,ChatGPT4帮我写的,并给了编译、运行方案:
1 | javac SpinLockAccumulator.java |
在MySQL INNODB 里怎么优化这个自旋锁
MySQL 在自旋锁抢锁的时候每次会调 ut_delay(底层会掉CPU指令,让CPU暂停一下但是不让出——避免上下文切换),发现性能好了几倍。这是MySQL 的官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-performance-spin_lock_polling.html
所以我们继续在以上代码的基础上在自旋的时候故意让CPU pause(50个), 这个优化详细案例:https://plantegg.github.io/2019/12/16/Intel%20PAUSE%E6%8C%87%E4%BB%A4%E5%8F%98%E5%8C%96%E6%98%AF%E5%A6%82%E4%BD%95%E5%BD%B1%E5%93%8D%E8%87%AA%E6%97%8B%E9%94%81%E4%BB%A5%E5%8F%8AMySQL%E7%9A%84%E6%80%A7%E8%83%BD%E7%9A%84/
该你动手了
随便找一台x86 机器,笔记本也可以,macOS 也行,核数多一些效果更明显。只要有Java环境,就用我编译好的class、libpause.so 理论上也行,不兼容的话按代码那一节再重新编译一下
可以做的实验:
- 重复我前面两个运行,看CPU 使用率以及最终耗时
- 尝试优化待pause版本的自旋锁实现,是不是要比没有pause性能反而要好
- 尝试让线程sleep 一下,效果是不是要好?
- 尝试减少线程数量,慢慢是不是发现自旋锁版本的性能越来越好了
改变线程数量运行对比:
1 | //自旋锁版本线程数对总时间影响很明显,且线程少的话性能要比加锁版本好,这符合自旋锁的设定:大概率不需要抢就用自旋锁 |
设定100并发下,改变机器核数对比:
1 | //16核机器跑3次 耗时稳定在12秒以上,CPU使用率 1600% |
总结
以后应该不会再对升配后CPU 使用率也上去了,但是最终效率反而没变展现得很惊诧了
从CPU 使用率、上下文切换上理解自旋锁(乐观锁)和锁(悲观锁)
MySQL 里对自旋锁的优化,增加配置 innodb_spin_wait_delay
来增加不同场景下DBA 的干预手段
这篇文章主要功劳要给 ChatGPT4 ,里面所有演示代码都是它完成的
相关阅读
流量一样但为什么CPU使用率差别很大 同样也是跟CPU 要效率,不过这个案例不是因为自旋锁导致CPU 率高,而是内存延时导致的
今日短平快,ECS从16核升配到48核后性能没有任何提升(Netflix) 也是CPU 使用率高没有产出,cacheline伪共享导致的
你要是把这个案例以及上面三个案例综合看明白了,相当于把计算机组成原理就学明白了。这里最核心的就是“内存墙”,也就是内存速度没有跟上CPU的发展速度,导致整个计算机内绝大多场景下读写内存缓慢成为主要的瓶颈
如果你觉得看完对你很有帮助可以通过如下方式找到我
find me on twitter: @plantegg
知识星球:https://t.zsxq.com/0cSFEUh2J
开了一个星球,在里面讲解一些案例、知识、学习方法,肯定没法让大家称为顶尖程序员(我自己都不是),只是希望用我的方法、知识、经验、案例作为你的垫脚石,帮助你快速、早日成为一个基本合格的程序员。
争取在星球内:
- 养成基本动手能力
- 拥有起码的分析推理能力–按我接触的程序员,大多都是没有逻辑的
- 知识上教会你几个关键的知识点